#include "stdafx.h"
#include "window.h"
#include "coresettings.h"
#include "../demo/tileengine.h"
#include <SDL/SDL.h>
#include <SDL/SDL_opengl.h>

#include "glc/clientstate.h"

#include <set>

namespace Core {

static const char WINDOW_TITLE[] = "Sample tile graphic application";

class WindowPrivate
{
public:
    bool m_isMinimized;
    bool m_shouldStopMainLoop;
    SDL_Surface *m_screen;
    Demo::TileEngine m_tileEngine;

    WindowPrivate()
        :m_isMinimized(false)
        ,m_shouldStopMainLoop(false)
        ,m_screen(0)
    {
    }

    void show()
    {
        resetEngine(&m_tileEngine);
    }

    bool pressed(SDLKey key)
    {
        return (m_pressed.count(key) != 0);
    }

    void onKeyUp(SDLKey key)
    {
        auto item = m_pressed.find(key);
        if (item != m_pressed.end())
        {
            m_pressed.erase(item);
        }
        else
        {
            myDebug() << "Fixme: WindowPrivate()::onKeyUp() called,"
                         "but this key isn't pressed: "
                      << static_cast<int>(key) << '\n';
        }
        m_engine->onKeyUp(key);
    }

    void onKeyDown(SDLKey key)
    {
        m_pressed.insert(key);
        if ((pressed(SDLK_RALT) || pressed(SDLK_LALT))
            && pressed(SDLK_F4))
        {
            exit(0);
        }
        else
        {
            m_engine->onKeyDown(key);
        }
    }

    void resetEngine(IEngine *inEngine)
    {
        m_engine = inEngine;
        m_engine->initialization(m_screen);
    }

    IEngine* engine()
    {
        return m_engine;
    }

private:
    std::set<SDLKey> m_pressed;
    IEngine *m_engine;
};

Window* Window::instance()
{
    static Window global;
    return &global;
}

bool Window::pressed(SDLKey key)
{
    return d->pressed(key);
}

void Window::init()
{
    SDL_WM_SetCaption(WINDOW_TITLE, 0);
}

void Window::show()
{
    int width = CoreSettings::instance()->windowWidth();
    int height = CoreSettings::instance()->windowHeight();

    if (CoreSettings::instance()->isFullscreen())
    {
        d->m_screen = SDL_SetVideoMode(width, height, 16,
                                     SDL_OPENGL | SDL_FULLSCREEN);
    }
    else
    {
        d->m_screen = SDL_SetVideoMode(width, height, 16, SDL_OPENGL);
    }
    if (!d->m_screen)
    {
        exit(0);
    }
    GLC::ClientState::resetViewport(0, 0, d->m_screen->w, d->m_screen->h);
    d->show();
}

static inline
void ConvertMouseEvent(const SDL_Event &event, MouseEvent &me)
{
    switch (event.button.button)
    {
        case SDL_BUTTON_LEFT:
            me.button = MouseButton_LEFT;
            break;
        case SDL_BUTTON_RIGHT:
            me.button = MouseButton_RIGHT;
            break;
        case SDL_BUTTON_MIDDLE:
            me.button = MouseButton_MIDDLE;
            break;
        case SDL_BUTTON_WHEELUP:
            me.button = MouseButton_WHEELUP;
            break;
        case SDL_BUTTON_WHEELDOWN:
            me.button = MouseButton_WHEELDOWN;
            break;
        default:
            me.button = MouseButton_NONE;
    }
    me.x = event.motion.x;
    me.y = event.motion.y;
    me.diffX = event.motion.xrel;
    me.diffY = event.motion.yrel;
}

void Window::enterLoop()
{
    SDL_Event event;
    bool quit = false;
    while (!quit)
    {
        while (SDL_PollEvent(&event))
        {
            switch (event.type)
            {
                case SDL_QUIT:
                    quit = true;
                break;
                case SDL_ACTIVEEVENT:
                {
                    if (event.active.gain)
                        d->engine()->draw();
                    break;
                }
                case SDL_KEYUP:
                {
                    d->onKeyUp(event.key.keysym.sym);
                    break;
                }
                case SDL_KEYDOWN:
                {
                    d->onKeyDown(event.key.keysym.sym);
                    break;
                }
                case SDL_MOUSEMOTION:
                {
                    MouseEvent me;
                    ConvertMouseEvent(event, me);
                    d->engine()->onMouseMove(me);
                    break;
                }
                case SDL_MOUSEBUTTONDOWN:
                {
                    MouseEvent me;
                    ConvertMouseEvent(event, me);
                    d->engine()->onMouseDown(me);
                    break;
                }
                case SDL_MOUSEBUTTONUP:
                {
                    MouseEvent me;
                    ConvertMouseEvent(event, me);
                    d->engine()->onMouseUp(me);
                    break;
                }
            }
            if (d->m_shouldStopMainLoop)
                quit = true;
                break;
        }
        d->engine()->onIdle();
    }
    d->engine()->destroy();
}

void Window::resetEngine(IEngine *engine)
{
    d->resetEngine(engine);
}

Window::Window()
    :d(new WindowPrivate())
{
}

Window::~Window()
{
}

}
